package wordcount

import (
	"strings"
	"unicode"
	"unicode/utf8"
	"fmt"
	"sort"
	"os"
	"log"
	"bufio"
	"io"
)

type Pair struct {
	Key string
	Value int
}

type PairList []Pair

func (p PairList) Swap(i, j int) {
	p[i], p[j] = p[j], p[i]
}

func (p PairList) Len() int {
	return len(p)
}

func (p PairList) Less(i, j int) bool {
	return p[j].Value < p[j].Value
}

// 提取单词
func SplitOnNonLetter(s string) []string {
	fieldFunc := func (char rune) bool {
		return !unicode.IsLetter(char)
	}
	return strings.FieldsFunc(s, fieldFunc)
}

type WordCount map[string]int

// 合并两个WordCount map
func (source WordCount) Merge(wordcount WordCount) WordCount {
	for k, v := range wordcount {
		source[k] += v
	}
	return source
}

// 词频统计
func (workcount WordCount) Report() {
	words := make([]string, 0, len(workcount))
	wordWidth, frequenctWidth := 0, 0
	for word, frequency := range workcount {
		words = append(words, word)
		if width := utf8.RuneCountInString(word); width > wordWidth {
			wordWidth = width
		}
		if width := len(fmt.Sprint(frequency)); width > frequenctWidth {
			frequenctWidth = width
		}
	}

	sort.Strings(words)
	gap := wordWidth + frequenctWidth - len("Word") - len("Frequency")
	fmt.Print("Word %*s%s\n", gap, " ", "Frequency")
	for _, word := range words {
		fmt.Printf("%-*s %*d\n", wordWidth, word, frequenctWidth, workcount[word])
	}
}

// 从多到少打印词频
func (wordcount WordCount) SortReport() {
	p := make(PairList, len(wordcount))
	i := 0

	for k, v := range wordcount {
		p[i] = Pair{k, v}
		i++
	}

	sort.Sort(p)

	wordWidth, frequencyWidth := 0, 0
	for _, pair := range p {
		word, frequency := pair.Key, pair.Value
		if width := utf8.RuneCountInString(word); width > wordWidth {
			wordWidth = width
		}
		if width := len(fmt.Sprint(frequency)); width > frequencyWidth {
			frequencyWidth = width
		}
	}

	gap := wordWidth + frequencyWidth - len("Word") - len("Frequency")
	fmt.Printf("Word %*s%s\n", gap, " ", "Frequency")

	for _, pair := range p {
		fmt.Printf("%-*s %*d\n", wordWidth, pair.Key, frequencyWidth,
			pair.Value)
	}
}

// 从文件读取单词,并更新其出现次数
func (wordcount WordCount) UpdateFreq(filename string) {
	var file *os.File
	var err error

	if file, err = os.Open(filename); err != nil {
		log.Println("failed to open the file:", err)
		return
	}

	defer file.Close()

	reader := bufio.NewReader(file)

	for {
		line, err := reader.ReadString('\n')
		for _, word := range SplitOnNonLetter(strings.TrimSpace(line)) {
			if len(word) > utf8.UTFMax || utf8.RuneCountInString(word) > 1 {
				wordcount[strings.ToLower(word)] += 1
			}
		}

		if err != nil {
			if err != io.EOF {
				log.Println("failed to finish reading the file: ", err)
			}
			break
		}
	}
}

// 并发统计单词频次
func (wordcount WordCount) WordFreqCounter(files []string) {
	result := make(chan Pair, len(files))
	done := make(chan struct{}, len(files))

	for i := 0; i < len(files); i++ {
		go func(done chan <- struct{}, result chan <-Pair, filename string) {
			wordcount := make(WordCount)
			wordcount.UpdateFreq(filename)
			for k, v := range wordcount {
				pair := Pair{k, v}
				result <- pair
			}
			done <- struct{}{}
		}(done, result, files[i])
	}

	for working := len(files); working > 0; {
		select {
		case pair := <- result:
			wordcount[pair.Key] += pair.Value
		case <- done:
			working--
		}
	}

	DONE:
	for {
		select {
		case pair := <-result:
			wordcount[pair.Key] += pair.Value
		default:
			break DONE
		}
	}

	close(result)
	close(done)
}